package AGENTS;

import java.io.*;

public class ART {

    private double[][] I;
    final int numSpace = 2;
    final int INPUT = 0;
    final int OUTPUT = 1;

    public int agentid = 0;

    public int numinputs;
    public int numOutputs = 2;

    final static int RFALCON = 0;
    final static int TDFALCON = 1;

    final int FUZZYART = 0;
    final int ART2 = 1;

    final int PERFORM = 0;
    final int LEARN = 1;
    final int INSERT = 2;

    private double[] T;
    private int[] numInput;
    private int numCode = 0;
    public double[][][] W;
    private int JJ;
    private boolean[] newCode;


    private int capacity = 2000;

    private double beta = (double) 1.0;
    private double epilson = (double) 0.001;
    private double gamma[] = {(double) 1.0, (double) 1.0};


    private double alpha[] = {(double) 0.1, (double) 0.1};
    private double b_rho[] = {(double) 0.2, (double) 0.5};

    public static boolean forgetting = false;
    public static boolean INTERFLAG = false;
    public static boolean detect_loop = false;
    public static boolean look_ahead = false;

    public double QAlpha = (double) 0.5;
    public double QGamma = (double) 0.1;

    public static double minQEpsilon = (double) 0.00500;
    public static double initialQ = (double) 0.5;
    public static double QEpsilonDecay = (double) 0.00050;
    public static double QEpsilon = (double) 0.50000;
    public int agent_num;

    double this_Q;
    double max_Q;
    double new_Q;

    //构造
    public ART(int num, int numinputs) throws IOException {
        agentid = num;

        this.numinputs = numinputs;

        numInput = new int[numSpace];
        numInput[0] = numinputs;
        numInput[1] = numOutputs;


        I = new double[capacity][];

        for (int i = 0; i < numSpace; i++)
            I[i] = new double[numInput[i]];

        if (!rw) {
            numCode = 0;
            newCode = new boolean[capacity];
            newCode[0] = true;

            T = new double[capacity];

            W = new double[capacity][][];

            for (int j = 0; j <= numCode; j++) {
                W[j] = new double[numSpace][];
                for (int k = 0; k < numSpace; k++) {
                    W[j][k] = new double[numInput[k]];
                    for (int i = 0; i < numInput[k]; i++)
                        W[j][k][i] = (double) 1.0;
                }
            }
        } else {
            readfile(agent_num);
        }
    }


    public void setParameters(int AVTYPE, boolean immediateReward) {

        if (AVTYPE == RFALCON) {
            QEpsilonDecay = (double) 0.00000;
            QEpsilon = (double) 0.00000;
        } else { //  QEpsilonDecay rate for TD-ART
            QEpsilonDecay = (double) 0.00050;
            QEpsilon = (double) 0.50000;
        }

        if (immediateReward)
            QGamma = (double) 0.5;
        else
            QGamma = (double) 0.9;
    }

    //提取环境信息
    public void setinputs(double[] input) {//提取状态
        for (int i = 0; i < numinputs; i++) {
            I[0][i] = input[i];
        }
    }

    public void setoutputs(double r) {//提取奖励
        I[OUTPUT][0] = r;
        I[OUTPUT][1] = 1 - r;
    }


    //表现部分

    //代码激活（F2节点思考T--activityF2）
    public void CodeActivition(int numSpace) {
        double top, bottom;
        for (int j = 0; j <= numCode; j++) {//激活函数
            for (int k = 0; k < numSpace; k++) {
                top = 0;
                bottom = (double) alpha[k];
                for (int i = 0; i < numInput[k]; i++) {
                    top += Math.min(I[k][i], W[j][k][i]);
                    bottom += W[j][k][i];
                }
                T[j] += gamma[k] * (double) (top / bottom); //Tj
            }
        }
    }


    //代码竞争（判断最好的思考）
    public int CodeCompetition() {
        double max_act = (double) -Double.MAX_VALUE;
        int JJ = 0;

        for (int j = 0; j <= numCode; j++)
            if (T[j] > max_act) {
                max_act = T[j];
                JJ = j;
            }
        return (JJ);
    }

    public boolean isNull(double[] x, int n) {
        for (int i = 0; i < n; i++)
            if (x[i] != 0) return (false);
        return (true);
    }

    //活动读出（提取记忆）
    public void WtoI(int j, int k) {
        for (int i = 0; i < numInput[k]; i++)
            I[k][i] = W[j][k][i];
    }


    public double ART_PERFORM(int J) {


        double outputs = (double) 0.0;

        CodeActivition(1); //代码激活（每个节点思考）
        JJ = CodeCompetition();

        //从记忆中提取Q值
        WtoI(JJ, OUTPUT);//F1节点提取记忆（Q值）
        if (I[OUTPUT][0] == I[OUTPUT][1] && I[OUTPUT][0] == 1) { //初次记忆
            outputs = (double) J / (double) capacity;
        } else
            outputs = I[OUTPUT][0];//有记忆就用记忆
        return (outputs);
    }


    //学习部分

    //模板匹配（判断是否合理）
    public double TemplateMatching(int k, int j) {
        double m = (double) 0.0;
        double denominator = (double) 0.0; //分母

        if (isNull(I[k], numInput[k]))
            return (1);

        for (int i = 0; i < numInput[k]; i++) {//相似度
            m = m + Math.min(I[k][i], W[j][k][i]);
            denominator = denominator + I[k][i];
        }

        if (denominator == 0)
            return (1);
        else
            return (m / denominator);
    }

    //模板学习（更新记忆）
    public void TemplateLearning(int JJ) {
        double rate;
        if (!newCode[JJ] || numCode < capacity) {
            if (newCode[JJ]) rate = 1;
            else rate = beta;
            for (int k = 0; k < numSpace; k++) {//再次记忆
                for (int i = 0; i < numInput[k]; i++) {
                    W[JJ][k][i] = (1 - rate) * W[JJ][k][i] + rate * Math.min(W[JJ][k][i], I[k][i]);
                }
            }
            if (newCode[JJ]) {//如果初次记忆记忆
                newCode[JJ] = false;
                createNewCode();//创建新的节点
            }
        }
    }

    //创建新节点    
    public void createNewCode() {

        numCode++;
        System.out.println("numEvent");
        System.out.println(numCode);


        newCode[numCode] = true;


        W[numCode] = new double[numSpace][];
        for (int k = 0; k < numSpace; k++) {
            W[numCode][k] = new double[numInput[k]];
            for (int i = 0; i < numInput[k]; i++)
                W[numCode][k][i] = (double) 1.0;
        }


    }


    //完全放弃以前的记忆
    public void doOverwrite(int JJ) {
        for (int k = 0; k < numSpace; k++)
            for (int i = 0; i < numInput[k]; i++)
                W[JJ][k][i] = I[k][i];
    }

    public double ART_LEARN() {
        boolean reset = true,
                perfectMismatch = false;
        double QValue = (double) 0.0;
        double[] rho = new double[numSpace];
        double[] match = new double[numSpace];

        for (int k = 0; k < numSpace; k++)
            rho[k] = b_rho[k];


        //CodeActivition(1); //代码激活（每个节点思考）

        //模板学习（再次记忆）
        while (reset && !perfectMismatch) {
            reset = false;
            JJ = CodeCompetition(); //代码竞争（判断最好的思考）
            for (int k = 0; k < numSpace; k++)
                match[k] = TemplateMatching(k, JJ);    //模板匹配（判断是否合理）
            if (match[INPUT] < rho[INPUT] || match[OUTPUT] < rho[OUTPUT]) {//如果相似度过低
                if (match[INPUT] == 1) {//状态相似度完全相同
                    perfectMismatch = true;
                } else {//相似度低就重新初始化并更新阈值函数并重新选择
                    System.out.println("error");
                    T[JJ] = -Double.MAX_VALUE;
                    reset = true;
                    for (int k = 0; k < 1; k++)
                        if (match[k] > rho[k])
                            rho[k] = Math.min(match[k] + epilson, 1);
                }
            }
        }
        if (!perfectMismatch) TemplateLearning(JJ);//再次记忆
        else doOverwrite(JJ);//再次记忆（不过完全放弃以前的记忆）
        return (QValue);
    }

    public boolean rw = false;

    public void readfile(int agent_num) throws IOException {
        String path = "d:/store" + agent_num + ".txt";
        FileInputStream in = new FileInputStream(path);
        DataInputStream din = new DataInputStream(in);
        numCode = din.readInt();
        newCode = new boolean[numCode + 1];
        newCode[0] = true;

        T = new double[numCode + 1];

        W = new double[numCode + 1][][];
        for (int j = 0; j <= numCode; j++) {
            W[j] = new double[numSpace][];
            for (int k = 0; k < numSpace; k++) {
                W[j][k] = new double[numInput[k]];
                for (int i = 0; i < numInput[k]; i++)
                    W[j][k][i] = (double) 1.0;
            }
        }

        for (int i = 0; i < numCode; i++) {
            newCode[i] = din.readBoolean();
            for (int j = 0; j < numSpace; j++) {
                for (int k = 0; k < numInput[j]; k++) {
                    W[i][j][k] = din.readDouble();
                }
            }
        }
        din.close();
        in.close();

    }

    public void writefile(int agent_num) throws Exception {
        String path = "d:/store" + agent_num + ".txt";
        FileOutputStream out = new FileOutputStream(path);
        DataOutputStream dout = new DataOutputStream(out);

        dout.writeInt(numCode);
        for (int i = 0; i < numCode; i++) {
            dout.writeBoolean(newCode[i]);
            for (int j = 0; j < numSpace; j++) {
                for (int k = 0; k < numInput[j]; k++) {
                    dout.writeDouble(W[i][j][k]);
                }
            }
        }
        dout.close();
        out.close();
    }
}